package com.chenfan.process.biz.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.ccp.common.exception.ApiException;
import com.chenfan.ccp.entity.BaseEntity;
import com.chenfan.ccp.util.tool.JsonUtil;
import com.chenfan.ccp.util.tool.UuidUtil;
import com.chenfan.infra.context.UserContextHolder;
import com.chenfan.infra.vo.UserVO;
import com.chenfan.process.biz.ApprovalTemplateBiz;
import com.chenfan.process.common.constants.CacheConstants;
import com.chenfan.process.common.exception.ProcessResultCode;
import com.chenfan.process.entity.dto.*;
import com.chenfan.process.entity.po.*;
import com.chenfan.process.entity.vo.ApprovalTemplateDetailVO;
import com.chenfan.process.entity.vo.ApprovalTemplateVo;
import com.chenfan.process.enums.*;
import com.chenfan.process.service.*;
import com.chenfan.process.util.NodeUtil;
import com.chenfan.process.util.TenantUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author liuming
 * @date 2021/11/25
 */
@Service
public class ApprovalTemplateBizImpl implements ApprovalTemplateBiz {
    @Autowired
    private ApprovalTemplateSnapshotService approvalTemplateSnapshotService;
    @Autowired
    private ApprovalTemplateService approvalTemplateService;
    @Autowired
    private ProcessService processService;
    @Autowired
    private ProcessRelatedService processRelatedService;
    @Autowired
    private ProcessFormNodeRelationService processFormNodeRelationService;
    @Autowired
    private ProcessCarbonCopyService processCarbonCopyService;
    @Autowired
    private ProcessRelatedAttributeService processRelatedAttributeService;
    @Autowired
    private ProcessFormAttributeService processFormAttributeService;
    @Autowired
    private ProcessFormService processFormService;
    @Autowired
    private ApprovalService approvalService;
    @Autowired
    private ProcessNodeNotifyConfigService processNodeNotifyConfigService;
    @Autowired
    private TemplateConfigService templateConfigService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long createApprovalTemplate(ApprovalTemplateReceivedDTO template) {
        // 检查是否已经存在模板
        ApprovalTemplatePo checkTemplate = approvalTemplateService.findByTitle(template.getTitle());
        if (Objects.nonNull(checkTemplate)) {
            throw new ApiException(ProcessResultCode.TEMPLATE_TITLE_EXISTS);
        }

        if (!StringUtils.isEmpty(template.getCode())) {
            // 检查对应的Code是否有审批流程
            ApprovalTemplatePo check = approvalTemplateService.getByCode(template.getCode());
            if (Objects.nonNull(check)) {
                // 已经有对应的审批流程
                throw new ApiException(ProcessResultCode.TEMPLATE_EXISTS);
            }
        }

        // 存储快照和模板
        ApprovalTemplatePo templatePo = ApprovalTemplatePo.of(template, Boolean.TRUE);
        TemplateConfigPo templateConfigPo = TemplateConfigPo.ofDefault(template);
        templateConfigPo.setTemplateId(templatePo.getId());
        ApprovalTemplateSnapshotPo snapshot = ApprovalTemplateSnapshotPo.of(template, templatePo);
        templatePo.setApprovalSnapshotId(snapshot.getId());
        templatePo.setTenantId(TenantUtil.getTenantId());
        this.verifyMessageUrl(templateConfigPo);
        approvalTemplateService.save(templatePo);
        templateConfigService.save(templateConfigPo);

        String processData = template.getProcessData();
        ApprovalTemplateDTO approvalTemplateDTO = JsonUtil.string2Obj(processData, ApprovalTemplateDTO.class);

        List<ApprovalTemplateDTO.LinkData> linkDataArray = approvalTemplateDTO.getLinkDataArray();
        List<ApprovalTemplateDTO.NodeData> nodeDataArray = approvalTemplateDTO.getNodeDataArray();
        NodeUtil.validateData(linkDataArray, nodeDataArray);

        List<ProcessPo> allProcessList = new ArrayList<>();
        List<ProcessFormNodeRelation> nodeRelations = new ArrayList<>();
        List<ProcessCarbonCopyPo> ccList = new ArrayList<>();
        List<ProcessNodeNotifyConfig> nodeNotifyConfigList = new ArrayList<>();

        Map<Long, ProcessLinkedDTO> processLinkMap = new HashMap<>();

        checkNodeTag(nodeDataArray);
        // 解析节点，存储节点
        nodeDataArray.forEach(e -> {
            // 审批人
            List<ApprovalTemplateDTO.Audit> auditList = e.getAuditList();
            List<ApprovalTemplateDTO.Option> options = e.getOption();
            List<ApprovalTemplateDTO.CarbonCopy> carbonCopyList = e.getCarbonCopyList();

            if (CollectionUtils.isEmpty(auditList)) {
                // TODO 有节点没有设置审批人,提示错误
            }

            // 当前节点. 当前节点保存了多少用户. 这些在后续的节点关系是需要的
            Long key = e.getKey();
            AtomicReference<Integer> linkType = new AtomicReference<>(0);
            Long groupId = UuidUtil.generateId();
            ProcessLinkedDTO.ProcessLinkedDTOBuilder builder = ProcessLinkedDTO.builder();
            List<ProcessPo> processList = auditList.stream().map(o -> {
                ProcessPo processPo = new ProcessPo();
                Long processId = UuidUtil.generateId();
                processPo.setId(processId);
                processPo.setProcessTitle(e.getText());
                processPo.setProcessTag(e.getTag());
//                e.setTag(StringUtils.isEmpty(e.getTag())? processId.toString() : e.getTag());
                // 部门负责人

                if (o.getType() < 0) {
                    processPo.setAssociateGroupId(o.getType().toString());
                    processPo.setAssociateGroupName(ProcessNodeEnums.getMessage(o.getType()));
                } else {
                    linkType.set(o.getType());
                    if (o.grouped()) {
                        builder.groupId(groupId);
                        processPo.setGroupId(groupId);
                    }
                    processPo.setAssociateGroupId(o.getUserId());
                    processPo.setAssociateGroupName(o.getUserName());
                }
                if (!CollectionUtils.isEmpty(e.getMessageTemplateList())) {
                    List<ProcessNodeNotifyConfig> processNodeList = e.getMessageTemplateList().stream().map(oo -> {
                        ProcessNodeNotifyConfig processNodeNotifyConfig = new ProcessNodeNotifyConfig();
                        BeanUtils.copyProperties(oo, processNodeNotifyConfig);
                        processNodeNotifyConfig.setApprovalTemplateId(templatePo.getId());
                        processNodeNotifyConfig.setProcessId(processPo.getId());
                        return processNodeNotifyConfig;
                    }).collect(Collectors.toList());

                    nodeNotifyConfigList.addAll(processNodeList);
                }


                // 设置节点属性关联关系
                options.forEach(ol -> {
                    ProcessFormNodeRelation processFormNodeRelation = new ProcessFormNodeRelation();
                    processFormNodeRelation.setId(UuidUtil.generateId());
                    processFormNodeRelation.setFormId(template.getFormId());
                    processFormNodeRelation.setFormAttributeId(Long.valueOf(ol.getId()));
                    processFormNodeRelation.setProcessId(processPo.getId());
                    processFormNodeRelation.setApprovalTemplateId(templatePo.getId());
                    processFormNodeRelation.setEditable(BooleanEnum.getValue(ol.getEditable()));
                    processFormNodeRelation.setRequired(BooleanEnum.getValue(ol.getRequired()));
                    // 设置参数
                    nodeRelations.add(processFormNodeRelation);
                });
                return processPo;
            }).collect(Collectors.toList());

            if (FrontNodeTypeEnums.isStart(e.getCategory())) {
                ProcessPo build = ProcessPo.builder().associateGroupId("0").build();
                build.setId(UuidUtil.generateId());
                processList.add(build);
            }

            if (FrontNodeTypeEnums.isEnd(e.getCategory())) {
                ProcessPo build = ProcessPo.builder().associateGroupId("-2").build();
                build.setId(UuidUtil.generateId());
                processList.add(build);
            }

            if (!CollectionUtils.isEmpty(processList)) {
                // 关系类型

                List<Long> processIdList = processList.stream().map(BaseEntity::getId).collect(Collectors.toList());
                ProcessLinkedDTO processLink = builder.processIdList(processIdList).linkType(linkType.get()).build();
                processLinkMap.put(key, processLink);
            }

            // 节点上有抄送的人员
            if (!CollectionUtils.isEmpty(carbonCopyList)) {
                // 设置对应的信息
                carbonCopyList.forEach(o -> processList.forEach(p -> {
                    ProcessCarbonCopyPo processCarbonCopyPo = new ProcessCarbonCopyPo();
                    processCarbonCopyPo.setApprovalTemplateId(templatePo.getId());
                    processCarbonCopyPo.setProcessId(p.getId());
                    processCarbonCopyPo.setRelatedId(o.getUserId());
                    processCarbonCopyPo.setRelatedName(o.getUserName());
                    processCarbonCopyPo.setUrl(o.getUrl());
                    if (o.isALl()) {
                        processCarbonCopyPo.setType(o.getType());
                    }
                    ccList.add(processCarbonCopyPo);
                }));
            }

            allProcessList.addAll(processList);
            // 存储process节点
        });
        snapshot.extractFromProcess(allProcessList);

        processService.saveBatch(allProcessList);


        // 保存关联关系
        if (!CollectionUtils.isEmpty(nodeRelations)) {
            processFormNodeRelationService.saveBatch(nodeRelations);
        }
        // 保存抄送节点
        if (!CollectionUtils.isEmpty(ccList)) {
            processCarbonCopyService.saveBatch(ccList);
        }

        Map<Long, ApprovalTemplateDTO.NodeData> processMap = nodeDataArray.stream().collect(Collectors.toMap(ApprovalTemplateDTO.NodeData::getKey, e -> e, (e1, e2) -> e2));

        List<ProcessRelatedAttribute> processRelatedAttributeList = new ArrayList<>();

        List<ProcessRelatedPo> processRelatedPoList = linkDataArray.stream()
            .filter(e -> processLinkMap.containsKey(e.getFrom()) && processLinkMap.containsKey(e.getTo())).map(e -> {
                Long from = e.getFrom();
                Long to = e.getTo();
                // 说明有条件分支

                ApprovalTemplateDTO.NodeData nodeData = processMap.get(from);
                ProcessLinkedDTO beforeLink = processLinkMap.get(from);
                List<Long> beforeList = beforeLink.getProcessIdList();
                ProcessLinkedDTO afterLink = processLinkMap.get(to);
                List<Long> afterList = afterLink.getProcessIdList();

                List<ProcessRelatedPo> relatedPoList = new ArrayList<>();

                for (Long before : beforeList) {
                    Long groupId = UuidUtil.generateId();
                    for (Long after : afterList) {
                        ProcessRelatedPo processRelatedPo = ProcessRelatedPo.ofDefault(before, after);

                        if (FrontNodeTypeEnums.isStart(nodeData.getCategory())) {
                            processRelatedPo.setProcessStatus(ProcessResultTypeEnums.PARENT.getIndex());
                        }
                        if (FrontNodeTypeEnums.isEnd(nodeData.getCategory())) {
                            processRelatedPo.setProcessStatus(ProcessResultTypeEnums.END.getIndex());
                        }
                        // 将节点上的分组分给节点关系分组上
                        processRelatedPo.setGroupId(afterLink.getGroupId());

                        // 判断如果有分支条件. 则优先取分支条件
                        if (!CollectionUtils.isEmpty(e.getAttributes())) {
                            processRelatedPo.setLinkType(LinkedTypeEnums.OPTION.getCode());
                            processRelatedPo.setType(afterLink.getType());
                            if (Objects.isNull(afterLink.getGroupId())) {
                                processRelatedPo.setGroupId(groupId);
                            }
                        } else {
                            processRelatedPo.setLinkType(afterLink.getType());
                        }
                        processRelatedPo.setApprovalTemplateId(templatePo.getId());
                        relatedPoList.add(processRelatedPo);
                    }
                }
                List<List<ApprovalTemplateDTO.LinkDataAttribute>> attributesnew = e.getAttributes();
                // 有关联属性
                if (!CollectionUtils.isEmpty(attributesnew)) {
                    List<List<ApprovalTemplateDTO.LinkDataAttribute>> attributes = attributesnew.stream().filter(o -> !CollectionUtils.isEmpty(o)).collect(Collectors.toList());
                    // TODO 通过表单属性查询属性信息
                    List<Long> formAttributes = attributes.stream().flatMap(e1 ->
                            e1.stream().map(o -> Long.valueOf(o.getFormAttributeId()))).collect(Collectors.toList()
                    );
                    if(!CollectionUtils.isEmpty(formAttributes)){
                        Map<Long, ProcessFormAttribute> relatedMap = processFormAttributeService.listByIds(formAttributes).stream().collect(Collectors.toMap(ProcessFormAttribute::getId, o -> o));
                        relatedPoList.forEach(p -> attributes.forEach(d -> {
                            Long groupId = UuidUtil.generateId();
                            d.stream().forEach(o -> {
                                ProcessRelatedAttribute relatedAttribute = new ProcessRelatedAttribute();
                                relatedAttribute.setRelatedId(p.getId());
                                relatedAttribute.setRelatedType(o.getType());
                                relatedAttribute.setRelatedName(relatedMap.get(Long.valueOf(o.getFormAttributeId())).getFieldName());
                                relatedAttribute.setApprovalTemplateId(templatePo.getId());
                                relatedAttribute.setFormAttributeId(Long.valueOf(o.getFormAttributeId()));
                                relatedAttribute.setRelatedValue(o.getValues());
                                relatedAttribute.setRelatedKey(relatedMap.get(Long.valueOf(o.getFormAttributeId())).getFieldKey());
                                relatedAttribute.setRelatedRelation(o.getRelation());
                                relatedAttribute.setGroupId(groupId);
                                processRelatedAttributeList.add(relatedAttribute);
                            });
                        }));
                    }
                }
                return relatedPoList;

            }).flatMap(Collection::stream).collect(Collectors.toList());

        processRelatedService.saveBatch(processRelatedPoList);

        if (!CollectionUtils.isEmpty(nodeNotifyConfigList)) {
            processNodeNotifyConfigService.saveBatch(nodeNotifyConfigList);
        }

        if (!CollectionUtils.isEmpty(processRelatedAttributeList)) {
            processRelatedAttributeService.saveBatch(processRelatedAttributeList);
        }
//        snapshot.setContent(JsonUtil.obj2String(approvalTemplateDTO));
        // 存储节点关联关系
        approvalTemplateSnapshotService.save(snapshot);

        CacheConstants.NODE_CACHE_LIST.cleanUp();
        return templatePo.getId();
    }

    /**
     * 校验跳转链接
     * @param config
     */
    private static void verifyMessageUrl(TemplateConfigPo config) {
        if(ObjectUtils.defaultIfNull(config.getAutoNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getApprovalNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getRemindNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getPassNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getRefuseNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getCcNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getOverDueNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getTransferNotify(), Boolean.FALSE).booleanValue()
        ) {
            if (StringUtils.isBlank(config.getMessageUrl())) {
                // 已经有对应的审批流程
                throw new ApiException(ProcessResultCode.MESSAGE_URL_NOT_NULL);
            }
        }
    }

    /**
     * 处理审批tag 保证不重复
     * @param allProcessList
     */
    private void checkNodeTag(List<ApprovalTemplateDTO.NodeData> nodeDataArray) {
        // 校验 nodeTag唯一性 唯一不重复
        List<String> nodeTags = nodeDataArray.stream().filter(e-> StringUtils.isNotEmpty(e.getTag())).collect(Collectors.groupingBy(ApprovalTemplateDTO.NodeData::getTag,Collectors.counting())).entrySet().stream().filter(entry -> entry.getValue() > 1).map(entry -> entry.getKey()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(nodeTags)) {
            throw new ApiException(ProcessResultCode.NODE_CODE_EXISTS);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateApprovalTemplate(Long approvalTemplateId, ApprovalTemplateReceivedDTO template) {
        // 存储快照和模板
        ApprovalTemplatePo templatePo = approvalTemplateService.getById(approvalTemplateId);

        // 看是否更新模板Code
        if (!StringUtils.isEmpty(template.getCode()) && !template.getCode().equals(templatePo.getCode())) {
            // 检查对应的Code是否有审批流程
            ApprovalTemplatePo check = approvalTemplateService.getByCode(template.getCode());
            if (Objects.nonNull(check)) {
                // 已经有对应的审批流程
                throw new ApiException(ProcessResultCode.TEMPLATE_EXISTS);
            }

            boolean match = approvalService.checkApproval(templatePo.getId());
            if (match) {
                // 说明已经有对应的审批,无法更改Code
                throw new ApiException(ProcessResultCode.ALREADY_EXIST_APPROVAL);
            }
            templatePo.setCode(template.getCode());
        }


        templatePo.setVersion(templatePo.getVersion() + 1);
        templatePo.setTitle(template.getTitle());
        ApprovalTemplateSnapshotPo snapshot = ApprovalTemplateSnapshotPo.of(template, templatePo);
        TemplateConfigPo templateConfig = TemplateConfigPo.ofDefault(template);
        templateConfig.setTemplateId(templatePo.getId());
        templateConfig.setVersion(templatePo.getVersion());

        templatePo.setApprovalSnapshotId(snapshot.getId());
//        templatePo.setApprovalNotify(template.getApprovalNotify());
//        templatePo.setRefuseNotify(template.getRefuseNotify());
//        templatePo.setPassNotify(template.getPassNotify());
//        templatePo.setCcNotify(template.getCcNotify());
//        templatePo.setRemindNotify(template.getRemindNotify());
//        templatePo.setOverDueNotify(template.getOverDueNotify());
        templatePo.setBelongId(template.getBelongId());
        templatePo.setBelongSystem(template.getBelongSystem());
        templatePo.setBelongSystemName(template.getBelongSystemName());

        snapshot.setContent(template.getProcessData());
        // 保存快照, 更新快照
        fillUpdateInfo(templatePo);
        this.verifyMessageUrl(templateConfig);

        approvalTemplateService.updateById(templatePo);
        templateConfigService.save(templateConfig);

        String processData = template.getProcessData();
        ApprovalTemplateDTO approvalTemplateDTO = JsonUtil.string2Obj(processData, ApprovalTemplateDTO.class);

        List<ApprovalTemplateDTO.LinkData> linkDataArray = approvalTemplateDTO.getLinkDataArray();
        List<ApprovalTemplateDTO.NodeData> nodeDataArray = approvalTemplateDTO.getNodeDataArray();
        NodeUtil.validateData(linkDataArray, nodeDataArray);

        List<ProcessPo> allProcessList = new ArrayList<>();
        List<ProcessFormNodeRelation> nodeRelations = new ArrayList<>();
        List<ProcessCarbonCopyPo> ccList = new ArrayList<>();
        List<ProcessNodeNotifyConfig> nodeNotifyConfigList = new ArrayList();

        Map<Long, ProcessLinkedDTO> processLinkMap = new HashMap<>();

        checkNodeTag(nodeDataArray);
        // 解析节点，存储节点
        nodeDataArray.forEach(e -> {
            // 审批人
            List<ApprovalTemplateDTO.Audit> auditList = e.getAuditList();
            List<ApprovalTemplateDTO.Option> options = e.getOption();
            List<ApprovalTemplateDTO.CarbonCopy> carbonCopyList = e.getCarbonCopyList();

            if (CollectionUtils.isEmpty(auditList)) {
                // TODO 有节点没有设置审批人,提示错误
            }

            // 当前节点. 当前节点保存了多少用户. 这些在后续的节点关系是需要的
            Long key = e.getKey();
            AtomicReference<Integer> linkType = new AtomicReference<>(0);
            Long groupId = UuidUtil.generateId();
            ProcessLinkedDTO.ProcessLinkedDTOBuilder builder = ProcessLinkedDTO.builder();
            List<ProcessPo> processList = auditList.stream().map(o -> {
                ProcessPo processPo = new ProcessPo();
                Long processId = UuidUtil.generateId();
                processPo.setId(processId);
                processPo.setProcessTitle(e.getText());
                processPo.setProcessTag(e.getTag());
                //e.setTag(StringUtils.isEmpty(e.getTag())? processId.toString() : e.getTag());
                // 部门负责人
                if (o.getType() < 0) {
                    processPo.setAssociateGroupId(o.getType().toString());
                    processPo.setAssociateGroupName(ProcessNodeEnums.getMessage(o.getType()));

                } else {
                    linkType.set(o.getType());
                    if (o.grouped()) {
                        builder.groupId(groupId);
                        processPo.setGroupId(groupId);
                    }
                    processPo.setAssociateGroupId(o.getUserId());
                    processPo.setAssociateGroupName(o.getUserName());
                }

                if (!CollectionUtils.isEmpty(e.getMessageTemplateList())) {
                    List<ProcessNodeNotifyConfig> processNodeList = e.getMessageTemplateList().stream().map(oo -> {
                        ProcessNodeNotifyConfig processNodeNotifyConfig = new ProcessNodeNotifyConfig();
                        BeanUtils.copyProperties(oo, processNodeNotifyConfig);
                        processNodeNotifyConfig.setApprovalTemplateId(templatePo.getId());
                        processNodeNotifyConfig.setProcessId(processPo.getId());
                        return processNodeNotifyConfig;
                    }).collect(Collectors.toList());

                    nodeNotifyConfigList.addAll(processNodeList);
                }
                // 设置节点属性关联关系
                options.forEach(ol -> {
                    ProcessFormNodeRelation processFormNodeRelation = new ProcessFormNodeRelation();
                    processFormNodeRelation.setId(UuidUtil.generateId());
                    processFormNodeRelation.setFormId(template.getFormId());
                    processFormNodeRelation.setFormAttributeId(Long.valueOf(ol.getId()));
                    processFormNodeRelation.setVersion(templatePo.getVersion());
                    processFormNodeRelation.setProcessId(processPo.getId());
                    processFormNodeRelation.setApprovalTemplateId(templatePo.getId());
                    processFormNodeRelation.setEditable(BooleanEnum.getValue(ol.getEditable()));
                    processFormNodeRelation.setRequired(BooleanEnum.getValue(ol.getRequired()));
                    nodeRelations.add(processFormNodeRelation);
                });
                return processPo;
            }).collect(Collectors.toList());

            if (FrontNodeTypeEnums.isStart(e.getCategory())) {
                ProcessPo build = ProcessPo.builder().associateGroupId("0").build();
                build.setId(UuidUtil.generateId());
                processList.add(build);
            }

            if (FrontNodeTypeEnums.isEnd(e.getCategory())) {
                ProcessPo build = ProcessPo.builder().associateGroupId("-2").build();
                build.setId(UuidUtil.generateId());
                processList.add(build);
            }

            if (!CollectionUtils.isEmpty(processList)) {
                // 关系类型

                List<Long> processIdList = processList.stream().map(BaseEntity::getId).collect(Collectors.toList());
                ProcessLinkedDTO processLink = builder.processIdList(processIdList).linkType(linkType.get()).build();
                processLinkMap.put(key, processLink);
            }

            // 节点上有抄送的人员
            if (!CollectionUtils.isEmpty(carbonCopyList)) {
                // 设置对应的信息
                carbonCopyList.forEach(o -> processList.forEach(p -> {
                    ProcessCarbonCopyPo processCarbonCopyPo = new ProcessCarbonCopyPo();
                    processCarbonCopyPo.setApprovalTemplateId(templatePo.getId());
                    processCarbonCopyPo.setProcessId(p.getId());
                    if (o.isALl()) {
                        processCarbonCopyPo.setType(o.getType());
                    }
                    processCarbonCopyPo.setRelatedId(o.getUserId());
                    processCarbonCopyPo.setRelatedName(o.getUserName());
                    processCarbonCopyPo.setUrl(o.getUrl());
                    ccList.add(processCarbonCopyPo);
                }));
            }

            allProcessList.addAll(processList);
            // 存储process节点
        });
        snapshot.extractFromProcess(allProcessList);
        processService.saveBatch(allProcessList);

        // 保存关联关系
        if (!CollectionUtils.isEmpty(nodeRelations)) {
            processFormNodeRelationService.saveBatch(nodeRelations);
        }
        // 保存抄送节点
        if (!CollectionUtils.isEmpty(ccList)) {
            processCarbonCopyService.saveBatch(ccList);
        }

        Map<Long, ApprovalTemplateDTO.NodeData> processMap = nodeDataArray.stream().collect(Collectors.toMap(ApprovalTemplateDTO.NodeData::getKey, e -> e, (e1, e2) -> e2));
        List<ProcessRelatedAttribute> processRelatedAttributeList = new ArrayList<>();

        List<ProcessRelatedPo> processRelatedPoList = linkDataArray.stream()
            .filter(e -> processLinkMap.containsKey(e.getFrom()) && processLinkMap.containsKey(e.getTo())).map(e -> {
                Long from = e.getFrom();
                Long to = e.getTo();
                // 说明有条件分支

                ApprovalTemplateDTO.NodeData nodeData = processMap.get(from);
                ProcessLinkedDTO beforeLink = processLinkMap.get(from);
                List<Long> beforeList = beforeLink.getProcessIdList();
                ProcessLinkedDTO afterLink = processLinkMap.get(to);
                List<Long> afterList = afterLink.getProcessIdList();

                List<ProcessRelatedPo> relatedPoList = new ArrayList<>();
                for (Long before : beforeList) {
                    Long groupId = UuidUtil.generateId();
                    for (Long after : afterList) {
                        ProcessRelatedPo processRelatedPo = ProcessRelatedPo.ofDefault(before, after);

                        if (FrontNodeTypeEnums.isStart(nodeData.getCategory())) {
                            processRelatedPo.setProcessStatus(ProcessResultTypeEnums.PARENT.getIndex());
                        }
                        if (FrontNodeTypeEnums.isEnd(nodeData.getCategory())) {
                            processRelatedPo.setProcessStatus(ProcessResultTypeEnums.END.getIndex());
                        }
                        processRelatedPo.setGroupId(afterLink.getGroupId());
                        // 判断如果有分支条件. 则优先取分支条件
                        if (!CollectionUtils.isEmpty(e.getAttributes())) {
                            processRelatedPo.setLinkType(LinkedTypeEnums.OPTION.getCode());
                            if (Objects.isNull(afterLink.getGroupId())) {
                                processRelatedPo.setGroupId(groupId);
                            }
                            processRelatedPo.setType(afterLink.getType());
                        } else {
                            processRelatedPo.setLinkType(afterLink.getType());
                        }
                        processRelatedPo.setApprovalTemplateId(templatePo.getId());
                        processRelatedPo.setVersion(templatePo.getVersion());
                        relatedPoList.add(processRelatedPo);
                    }
                }

                List<List<ApprovalTemplateDTO.LinkDataAttribute>> attributesnew = e.getAttributes();

                // 有关联属性
                if (!CollectionUtils.isEmpty(attributesnew)) {
                    List<List<ApprovalTemplateDTO.LinkDataAttribute>> attributes = attributesnew.stream().filter(o -> !CollectionUtils.isEmpty(o)).collect(Collectors.toList());
                    // TODO 通过表单属性查询属性信息
                    List<Long> formAttributes = attributes.stream().flatMap(e1 ->
                            e1.stream().map(o -> Long.valueOf(o.getFormAttributeId()))).collect(Collectors.toList()
                    );
                    if(!CollectionUtils.isEmpty(formAttributes)){
                        Map<Long, ProcessFormAttribute> relatedMap = processFormAttributeService.listByIds(formAttributes).stream().collect(Collectors.toMap(ProcessFormAttribute::getId, o -> o));
                        relatedPoList.forEach(p -> attributes.forEach(d -> {
                            Long groupId = UuidUtil.generateId();
                            d.forEach(o -> {
                                ProcessRelatedAttribute relatedAttribute = new ProcessRelatedAttribute();
                                relatedAttribute.setRelatedId(p.getId());
                                relatedAttribute.setRelatedType(o.getType());
                                relatedAttribute.setRelatedName(relatedMap.get(Long.valueOf(o.getFormAttributeId())).getFieldName());
                                relatedAttribute.setApprovalTemplateId(templatePo.getId());
                                relatedAttribute.setFormAttributeId(Long.valueOf(o.getFormAttributeId()));
                                relatedAttribute.setRelatedValue(o.getValues());
                                relatedAttribute.setRelatedKey(relatedMap.get(Long.valueOf(o.getFormAttributeId())).getFieldKey());
                                relatedAttribute.setRelatedRelation(o.getRelation());
                                relatedAttribute.setGroupId(groupId);
                                processRelatedAttributeList.add(relatedAttribute);
                            });
                        }));
                    }

                }
                return relatedPoList;

            }).flatMap(Collection::stream).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(nodeNotifyConfigList)) {
            processNodeNotifyConfigService.saveBatch(nodeNotifyConfigList);
        }
        processRelatedService.saveBatch(processRelatedPoList);
//        snapshot.setContent(JsonUtil.obj2String(approvalTemplateDTO));
        approvalTemplateSnapshotService.save(snapshot);

        if (!CollectionUtils.isEmpty(processRelatedAttributeList)) {
            processRelatedAttributeService.saveBatch(processRelatedAttributeList);
        }
        CacheConstants.NODE_CACHE_LIST.cleanUp();
    }

    /**
     * 填充更新信息
     * @param templatePo
     */
    private static void fillUpdateInfo(ApprovalTemplatePo templatePo) {
        UserVO userVo = UserContextHolder.getUserVo();
        if(Objects.nonNull(userVo)) {
            templatePo.setUpdateBy(userVo.getUserId());
            templatePo.setUpdateName(userVo.getRealName());
            templatePo.setUpdateTime(new Date());
        }
    }

    @Override
    public ApprovalTemplateDetailVO getApprovalTemplate(Long approvalTemplateId) {
        ApprovalTemplatePo templatePo = approvalTemplateService.getById(approvalTemplateId);
        Long approvalSnapshotId = templatePo.getApprovalSnapshotId();

        ApprovalTemplateDetailVO detailVo = new ApprovalTemplateDetailVO();
        detailVo.setAutoMessage(Boolean.FALSE);
        TemplateConfigPo config = templateConfigService.getByTemplateId(templatePo.getId(), templatePo.getVersion());
        if (Objects.nonNull(config)) {
            BeanUtils.copyProperties(config, detailVo);
            if(ObjectUtils.defaultIfNull(config.getAutoNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getApprovalNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getRemindNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getPassNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getRefuseNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getCcNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getOverDueNotify(), Boolean.FALSE).booleanValue()
                || ObjectUtils.defaultIfNull(config.getTransferNotify(), Boolean.FALSE).booleanValue()
            ) {
                detailVo.setAutoMessage(Boolean.TRUE);
            }
        }
        BeanUtils.copyProperties(templatePo, detailVo);
        ApprovalTemplateSnapshotPo approvalSnapshot = approvalTemplateSnapshotService.getById(approvalSnapshotId);
        if (Objects.nonNull(approvalSnapshot)) {
            detailVo.setContent(approvalSnapshot.getContent());
            detailVo.setFormId(approvalSnapshot.getFormId());
        }

        return detailVo;
    }

    @Override
    public Page<ApprovalTemplateVo> pageApprovalTemplate(Integer pageNum, Integer pageSize,
        ApprovalTemplateQueryDTO template) {
        Page<ApprovalTemplateVo> page = new Page<>();

        Page<ApprovalTemplatePo> approvalTemplatePage =
            approvalTemplateService.pageApprovalTemplate(pageNum, pageSize, template);
        List<ApprovalTemplatePo> records = approvalTemplatePage.getRecords();

        page.setCurrent(approvalTemplatePage.getCurrent());
        page.setPages(approvalTemplatePage.getPages());
        page.setSize(approvalTemplatePage.getSize());
        page.setTotal(approvalTemplatePage.getTotal());

        if (CollectionUtils.isEmpty(records)) {
            page.setRecords(Collections.emptyList());
            return page;
        }

        List<Long> snapshotIdList =
            records.stream().map(ApprovalTemplatePo::getApprovalSnapshotId).collect(Collectors.toList());
        // 查询出对应的表单信息
        List<ApprovalTemplateSnapshotPo> approvalTemplateSnapshots = approvalTemplateSnapshotService.listByIds(snapshotIdList);
        List<Long> formIdList = approvalTemplateSnapshots.stream().map(ApprovalTemplateSnapshotPo::getFormId).collect(Collectors.toList());
        Map<Long, ApprovalTemplateSnapshotPo> snapshotMap = approvalTemplateSnapshots.stream().collect(Collectors.toMap(BaseEntity::getId, e -> e));

        Map<Long, ProcessForm> formMap = processFormService.listByIds(formIdList).stream().collect(Collectors.toMap(ProcessForm::getId, e -> e));
        List<ApprovalTemplateVo> templateList = records.stream().map(e -> {
            ApprovalTemplateVo approvalTemplateVo = e.copyApprovalTemplateVo();
            ApprovalTemplateSnapshotPo approvalTemplateSnapshotPo = snapshotMap.get(e.getApprovalSnapshotId());
            if (Objects.nonNull(approvalTemplateSnapshotPo) && Objects.nonNull(approvalTemplateSnapshotPo.getFormId())) {
                String formName = formMap.getOrDefault(approvalTemplateSnapshotPo.getFormId(), new ProcessForm()).getFormName();
                approvalTemplateVo.setFormName(formName);
            }
            return approvalTemplateVo;
        }).collect(Collectors.toList());
        page.setRecords(templateList);
        return page;
    }

    @Override
    public void updateApprovalTemplateCode(Long approvalTemplateId, ApprovalTemplateCodeDTO templateCode) {
        String code = templateCode.getCode();
        ApprovalTemplatePo templatePo = approvalTemplateService.getById(approvalTemplateId);

        if (Objects.isNull(templatePo)) {
            throw new ApiException(ProcessResultCode.APPROVAL_TEMPLATE_NOT_FOUND);
        }
        if (templatePo.getCode().equals(code)) {
            return;
        }
        // 检查对应的Code是否有审批流程
        ApprovalTemplatePo check = approvalTemplateService.getByCode(code);
        if (Objects.nonNull(check)) {
            // 已经有对应的审批流程
            throw new ApiException(ProcessResultCode.TEMPLATE_EXISTS);
        }
        templatePo.setCode(code);

        boolean match = approvalService.checkApproval(templatePo.getId());
        if (match) {
            // 说明已经有对应的审批,无法更改Code
            throw new ApiException(ProcessResultCode.ALREADY_EXIST_APPROVAL);
        }
        fillUpdateInfo(templatePo);
        // 检查是否已经发起审批
        approvalTemplateService.updateById(templatePo);
    }

    @Override
    public void preCheck(ApprovalTemplateCodeDTO dto) {

        if (!StringUtils.isEmpty(dto.getCode())) {
            String code = dto.getCode();
            ApprovalTemplatePo templatePo = approvalTemplateService.getByCode(code);
            if (Objects.nonNull(templatePo)) {
                throw new ApiException(ProcessResultCode.TEMPLATE_EXISTS);
            }
        }
        if (!StringUtils.isEmpty(dto.getTitle())) {
            ApprovalTemplatePo checkTemplate = approvalTemplateService.findByTitle(dto.getTitle());
            if (Objects.nonNull(checkTemplate)) {
                throw new ApiException(ProcessResultCode.TEMPLATE_TITLE_EXISTS);
            }
        }
    }

    @Override
    public void switchStatus(Long approvalTemplateId, Integer status) {
        ApprovalTemplatePo approvalTemplatePo = getAndCheck(approvalTemplateId);
        if ((approvalTemplatePo.getStatus() ^ status) == 0) {
            throw new ApiException(ProcessResultCode.STATUS_NOT_CHANGE);
        }
        approvalTemplatePo.setStatus(status);
        fillUpdateInfo(approvalTemplatePo);
        approvalTemplateService.updateById(approvalTemplatePo);
    }

    private ApprovalTemplatePo getAndCheck(Long approvalTemplateId) {
        ApprovalTemplatePo approvalTemplatePo = approvalTemplateService.getById(approvalTemplateId);
        if (Objects.isNull(approvalTemplatePo)) {
            throw new ApiException(ProcessResultCode.TEMPLATE_NOT_EXISTS);
        }
        return approvalTemplatePo;
    }

    @Override
    public Integer statisticsNumber() {
        return approvalTemplateService.statisticsNumber();
    }
}
